/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(
    dead_code,
    mutable_transmutes,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused_assignments,
    unused_mut,
    clippy::never_loop,
    clippy::if_same_then_else
)]
use crate::common::*;
extern "C" {
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    fn free(_: *mut libc::c_void);
    fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn strchr(_: *const libc::c_char, _: libc::c_int) -> *mut libc::c_char;
    fn strpbrk(_: *const libc::c_char, _: *const libc::c_char) -> *mut libc::c_char;
    fn strcasecmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn sysconf(__name: libc::c_int) -> libc::c_long;
    fn getdomainname(__name: *mut libc::c_char, __len: size_t) -> libc::c_int;
    fn innetgr(
        __netgroup: *const libc::c_char,
        __host: *const libc::c_char,
        __user: *const libc::c_char,
        __domain: *const libc::c_char,
    ) -> libc::c_int;
    fn alias_get(
        parse_tree: *mut sudoers_parse_tree,
        name: *const libc::c_char,
        type_0: libc::c_int,
    ) -> *mut alias;
    fn alias_put(a: *mut alias);
    fn addr_matches(n: *mut libc::c_char) -> bool;
    fn command_matches(
        sudoers_cmnd: *const libc::c_char,
        sudoers_args: *const libc::c_char,
        digest: *const command_digest,
    ) -> bool;
    static mut sudo_defs_table: [sudo_defs_types; 0];
    static mut sudoers_subsystem_ids: [libc::c_uint; 0];
    fn sudo_debug_printf2_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        level: libc::c_int,
        fmt: *const libc::c_char,
        _: ...
    );
    fn sudo_strtoid_v2(str: *const libc::c_char, errstr: *mut *const libc::c_char) -> id_t;
    fn sudo_pw_delref(_: *mut passwd);
    fn user_in_group(_: *const passwd, _: *const libc::c_char) -> bool;
    fn sudo_getpwnam(_: *const libc::c_char) -> *mut passwd;
    fn sudo_get_gidlist(pw: *const passwd, type_0: libc::c_uint) -> *mut gid_list;
    fn sudo_gidlist_delref(_: *mut gid_list);
    fn sudo_gidlist_addref(_: *mut gid_list);
    fn group_plugin_query(
        user: *const libc::c_char,
        group: *const libc::c_char,
        pwd: *const passwd,
    ) -> libc::c_int;
    static mut sudo_user: sudo_user;
    fn fnmatch(
        __pattern: *const libc::c_char,
        __name: *const libc::c_char,
        __flags: libc::c_int,
    ) -> libc::c_int;
}

pub const ENTRY_TYPE_QUERIED: libc::c_int = 0x01;

pub const RUNAS_USER_SPECIFIED: libc::c_int = 0x01;
pub const RUNAS_GROUP_SPECIFIED: libc::c_int = 0x02;

pub const ALLOW: libc::c_int = 1;
pub const DENY: libc::c_int = 0;

pub const FNM_CASEFOLD: libc::c_int = 1 << 4;
pub const _SC_HOST_NAME_MAX: libc::c_int = 180;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudoers_parse_tree {
    pub userspecs: userspec_list,
    pub defaults: defaults_list,
    pub aliases: *mut rbtree,
    pub shost: *const libc::c_char,
    pub lhost: *const libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct userspec_list {
    pub tqh_first: *mut userspec,
    pub tqh_last: *mut *mut userspec,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct userspec {
    pub entries: C2RustUnnamed_6,
    pub users: member_list,
    pub privileges: privilege_list,
    pub comments: comment_list,
    pub lineno: libc::c_int,
    pub file: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct comment_list {
    pub stqh_first: *mut sudoers_comment,
    pub stqh_last: *mut *mut sudoers_comment,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudoers_comment {
    pub entries: C2RustUnnamed_3,
    pub str_0: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_3 {
    pub stqe_next: *mut sudoers_comment,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct privilege_list {
    pub tqh_first: *mut privilege,
    pub tqh_last: *mut *mut privilege,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct privilege {
    pub entries: C2RustUnnamed_5,
    pub ldap_role: *mut libc::c_char,
    pub hostlist: member_list,
    pub cmndlist: cmndspec_list,
    pub defaults: defaults_list,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct cmndspec_list {
    pub tqh_first: *mut cmndspec,
    pub tqh_last: *mut *mut cmndspec,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct cmndspec {
    pub entries: C2RustUnnamed_4,
    pub runasuserlist: *mut member_list,
    pub runasgrouplist: *mut member_list,
    pub cmnd: *mut member,
    pub tags: cmndtag,
    pub timeout: libc::c_int,
    pub notbefore: time_t,
    pub notafter: time_t,
    pub role: *mut libc::c_char,
    pub type_0: *mut libc::c_char,
}
#[derive(Copy, Clone, BitfieldStruct)]
#[repr(C)]
pub struct cmndtag {
    #[bitfield(name = "nopasswd", ty = "libc::c_int", bits = "0..=2")]
    #[bitfield(name = "noexec", ty = "libc::c_int", bits = "3..=5")]
    #[bitfield(name = "setenv", ty = "libc::c_int", bits = "6..=8")]
    #[bitfield(name = "log_input", ty = "libc::c_int", bits = "9..=11")]
    #[bitfield(name = "log_output", ty = "libc::c_int", bits = "12..=14")]
    #[bitfield(name = "send_mail", ty = "libc::c_int", bits = "15..=17")]
    #[bitfield(name = "follow", ty = "libc::c_int", bits = "18..=20")]
    pub nopasswd_noexec_setenv_log_input_log_output_send_mail_follow: [u8; 3],
    #[bitfield(padding)]
    pub c2rust_padding: [u8; 1],
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_4 {
    pub tqe_next: *mut cmndspec,
    pub tqe_prev: *mut *mut cmndspec,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_5 {
    pub tqe_next: *mut privilege,
    pub tqe_prev: *mut *mut privilege,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_6 {
    pub tqe_next: *mut userspec,
    pub tqe_prev: *mut *mut userspec,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct command_digest {
    pub digest_type: libc::c_uint,
    pub digest_str: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_command {
    pub cmnd: *mut libc::c_char,
    pub args: *mut libc::c_char,
    pub digest: *mut command_digest,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_user {
    pub pw: *mut passwd,
    pub _runas_pw: *mut passwd,
    pub _runas_gr: *mut group,
    pub cmnd_stat: *mut stat,
    pub name: *mut libc::c_char,
    pub path: *mut libc::c_char,
    pub tty: *mut libc::c_char,
    pub ttypath: *mut libc::c_char,
    pub host: *mut libc::c_char,
    pub shost: *mut libc::c_char,
    pub runhost: *mut libc::c_char,
    pub srunhost: *mut libc::c_char,
    pub prompt: *mut libc::c_char,
    pub cmnd: *mut libc::c_char,
    pub cmnd_args: *mut libc::c_char,
    pub cmnd_base: *mut libc::c_char,
    pub cmnd_safe: *mut libc::c_char,
    pub class_name: *mut libc::c_char,
    pub krb5_ccname: *mut libc::c_char,
    pub gid_list: *mut gid_list,
    pub env_vars: *const *mut libc::c_char,
    pub role: *mut libc::c_char,
    pub type_0: *mut libc::c_char,
    pub cwd: *const libc::c_char,
    pub iolog_file: *mut libc::c_char,
    pub gids: *mut gid_t,
    pub execfd: libc::c_int,
    pub ngids: libc::c_int,
    pub closefrom: libc::c_int,
    pub lines: libc::c_int,
    pub cols: libc::c_int,
    pub flags: libc::c_int,
    pub max_groups: libc::c_int,
    pub timeout: libc::c_int,
    pub umask: mode_t,
    pub uid: uid_t,
    pub gid: uid_t,
    pub sid: pid_t,
}

static mut empty: member_list = member_list {
    tqh_first: 0 as *const member as *mut member,
    tqh_last: 0 as *const *mut member as *mut *mut member,
};

/*
 * Check whether user described by pw matches member.
 * Returns ALLOW, DENY or UNSPEC.
 */
#[no_mangle]
pub fn user_matches(
    mut parse_tree: *mut sudoers_parse_tree,
    mut pw: *const passwd,
    mut m: *const member,
) -> libc::c_int {
    unsafe {
        let mut lhost: *const libc::c_char = if !((*parse_tree).lhost).is_null() {
            (*parse_tree).lhost
        } else {
            sudo_user.runhost as *const libc::c_char
        };
        let mut shost: *const libc::c_char = if !((*parse_tree).shost).is_null() {
            (*parse_tree).shost
        } else {
            sudo_user.srunhost as *const libc::c_char
        };
        let mut matched: libc::c_int = UNSPEC;
        let mut a: *mut alias = std::ptr::null_mut::<alias>();
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        loop {
            match (*m).type0 as libc::c_int {
                ALL => {
                    matched = ((*m).negated == 0) as libc::c_int;
                }
                NETGROUP => {
                    if netgr_matches(
                        (*m).name,
                        if def_netgroup_tuple!() != 0 {
                            lhost
                        } else {
                            std::ptr::null::<libc::c_char>()
                        },
                        if def_netgroup_tuple!() != 0 {
                            shost
                        } else {
                            std::ptr::null::<libc::c_char>()
                        },
                        (*pw).pw_name,
                    ) {
                        matched = ((*m).negated == 0) as libc::c_int;
                    }
                }
                USERGROUP => {
                    if usergr_matches((*m).name, (*pw).pw_name, pw) {
                        matched = ((*m).negated == 0) as libc::c_int;
                    }
                }
                ALIAS | WORD => {
                    if (*m).type0 as libc::c_int == ALIAS {
                        a = alias_get(parse_tree, (*m).name, USERALIAS);
                        if !a.is_null() {
                            /* XXX */
                            let mut rc: libc::c_int =
                                userlist_matches(parse_tree, pw, &(*a).members);
                            if rc != UNSPEC {
                                matched = if (*m).negated as libc::c_int != 0 {
                                    (rc == 0) as libc::c_int
                                } else {
                                    rc
                                };
                            }
                            alias_put(a);
                            break;
                        }
                    }
                    /* FALLTHROUGH */
                    if userpw_matches((*m).name, (*pw).pw_name, pw) {
                        matched = ((*m).negated == 0) as libc::c_int;
                    }
                }
                _ => {}
            }
            break;
        }

        debug_return_int!(matched);
    } //unsafe
}

/*
 * Check for user described by pw in a list of members.
 * Returns ALLOW, DENY or UNSPEC.
 */
#[no_mangle]
pub fn userlist_matches(
    mut parse_tree: *mut sudoers_parse_tree,
    mut pw: *const passwd,
    mut list: *const member_list,
) -> libc::c_int {
    let mut m: *mut member = std::ptr::null_mut::<member>();
    let mut matched: libc::c_int = UNSPEC;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        m = *(*((*list).tqh_last as *mut member_list)).tqh_last;
        while !m.is_null() {
            matched = user_matches(parse_tree, pw, m);
            if matched != UNSPEC {
                break;
            }
            m = *(*((*m).entries.tqe_prev as *mut member_list)).tqh_last;
        }
        debug_return_int!(matched);
    } //unsafe
}

#[no_mangle]
pub fn runas_getgroups() -> *mut gid_list {
    let mut pw: *const passwd = std::ptr::null::<passwd>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        if def_preserve_groups!() != 0 {
            sudo_gidlist_addref(sudo_user.gid_list);
            debug_return_ptr!(sudo_user.gid_list);
        }

        /* Only use results from a group db query, not the front end. */
        pw = if !(sudo_user._runas_pw).is_null() {
            sudo_user._runas_pw
        } else {
            sudo_user.pw
        };
        debug_return_ptr!(sudo_get_gidlist(pw, ENTRY_TYPE_QUERIED as libc::c_uint));
    } //unsafe
}

/*
 * Check for user described by pw in a list of members.
 * If both lists are empty compare against def_runas_default.
 * Returns ALLOW, DENY or UNSPEC.
 */
#[no_mangle]
pub fn runaslist_matches(
    mut parse_tree: *mut sudoers_parse_tree,
    mut user_list: *const member_list,
    mut group_list: *const member_list,
    mut matching_user: *mut *mut member,
    mut matching_group: *mut *mut member,
) -> libc::c_int {
    unsafe {
        let mut lhost: *const libc::c_char = if !((*parse_tree).lhost).is_null() {
            (*parse_tree).lhost
        } else {
            sudo_user.runhost as *const libc::c_char
        };
        let mut shost: *const libc::c_char = if !((*parse_tree).shost).is_null() {
            (*parse_tree).shost
        } else {
            sudo_user.srunhost as *const libc::c_char
        };
        let mut user_matched: libc::c_int = UNSPEC;
        let mut group_matched: libc::c_int = UNSPEC;
        let mut m: *mut member = std::ptr::null_mut::<member>();
        let mut a: *mut alias = std::ptr::null_mut::<alias>();
        let mut rc: libc::c_int = 0;
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        if ISSET!(sudo_user.flags, RUNAS_USER_SPECIFIED) != 0
            || ISSET!(sudo_user.flags, RUNAS_GROUP_SPECIFIED) == 0
        {
            /* If no runas user or runas group listed in sudoers, use default. */
            if user_list.is_null() && group_list.is_null() {
                debug_return_int!(userpw_matches(
                    def_runas_default!(),
                    (*sudo_user._runas_pw).pw_name,
                    sudo_user._runas_pw
                ) as libc::c_int);
            }

            if !user_list.is_null() {
                m = *(*((*user_list).tqh_last as *mut member_list)).tqh_last;
                while !m.is_null() {
                    loop {
                        match (*m).type0 as libc::c_int {
                            ALL => {
                                user_matched = ((*m).negated == 0) as libc::c_int;
                            }
                            NETGROUP => {
                                if netgr_matches(
                                    (*m).name,
                                    if def_netgroup_tuple!() != 0 {
                                        lhost
                                    } else {
                                        std::ptr::null::<libc::c_char>()
                                    },
                                    if def_netgroup_tuple!() != 0 {
                                        shost
                                    } else {
                                        std::ptr::null::<libc::c_char>()
                                    },
                                    (*sudo_user._runas_pw).pw_name,
                                ) {
                                    user_matched = ((*m).negated == 0) as libc::c_int;
                                }
                            }
                            USERGROUP => {
                                if usergr_matches(
                                    (*m).name,
                                    (*sudo_user._runas_pw).pw_name,
                                    sudo_user._runas_pw,
                                ) {
                                    user_matched = ((*m).negated == 0) as libc::c_int;
                                }
                            }
                            ALIAS | WORD => {
                                if (*m).type0 as libc::c_int == ALIAS {
                                    a = alias_get(parse_tree, (*m).name, RUNASALIAS);
                                    if !a.is_null() {
                                        rc = runaslist_matches(
                                            parse_tree,
                                            &(*a).members,
                                            &empty,
                                            matching_user,
                                            std::ptr::null_mut::<*mut member>(),
                                        );
                                        if rc != UNSPEC {
                                            user_matched = if (*m).negated as libc::c_int != 0 {
                                                (rc == 0) as libc::c_int
                                            } else {
                                                rc
                                            };
                                        }
                                        alias_put(a);
                                        break;
                                    }
                                }
                                /* FALLTHROUGH */
                                if userpw_matches(
                                    (*m).name,
                                    (*sudo_user._runas_pw).pw_name,
                                    sudo_user._runas_pw,
                                ) {
                                    user_matched = ((*m).negated == 0) as libc::c_int;
                                }
                            }
                            MYSELF => {
                                if ISSET!(sudo_user.flags, RUNAS_USER_SPECIFIED) == 0
                                    || strcmp(sudo_user.name, (*sudo_user._runas_pw).pw_name)
                                        == 0 as libc::c_int
                                {
                                    user_matched = ((*m).negated == 0) as libc::c_int;
                                }
                            }
                            _ => {}
                        }
                        break;
                    }
                    if user_matched != UNSPEC {
                        if !matching_user.is_null() && (*m).type0 as libc::c_int != ALIAS {
                            *matching_user = m;
                        }
                        break;
                    } else {
                        m = *(*((*m).entries.tqe_prev as *mut member_list)).tqh_last;
                    }
                }
            }
        }

        /*
         * Skip checking runas group if none was specified.
         */
        if ISSET!(sudo_user.flags, RUNAS_GROUP_SPECIFIED) != 0 {
            /*if user_matched == UNSPEC {
                if strcmp(sudo_user.name, (*sudo_user._runas_pw).pw_name) == 0 {
                    user_matched = ALLOW; /* only changing group */
                }
            }*/
            if user_matched == UNSPEC && strcmp(sudo_user.name, (*sudo_user._runas_pw).pw_name) == 0
            {
                user_matched = ALLOW; /* only changing group */
            }

            if !group_list.is_null() {
                m = *(*((*group_list).tqh_last as *mut member_list)).tqh_last;
                while !m.is_null() {
                    loop {
                        match (*m).type0 as libc::c_int {
                            ALL => {
                                group_matched = ((*m).negated == 0) as libc::c_int;
                            }
                            ALIAS | WORD => {
                                if (*m).type0 as libc::c_int == ALIAS {
                                    a = alias_get(parse_tree, (*m).name, RUNASALIAS);
                                    if !a.is_null() {
                                        rc = runaslist_matches(
                                            parse_tree,
                                            &empty,
                                            &(*a).members,
                                            std::ptr::null_mut::<*mut member>(),
                                            matching_group,
                                        );
                                        if rc != UNSPEC {
                                            group_matched = if (*m).negated as libc::c_int != 0 {
                                                (rc == 0) as libc::c_int
                                            } else {
                                                rc
                                            };
                                        }
                                        alias_put(a);
                                        break;
                                    }
                                }
                                /* FALLTHROUGH */
                                if group_matches((*m).name, sudo_user._runas_gr) {
                                    group_matched = ((*m).negated == 0) as libc::c_int;
                                }
                            }
                            _ => {}
                        }
                        break;
                    }
                    if group_matched != UNSPEC {
                        if !matching_group.is_null() && (*m).type0 as libc::c_int != ALIAS {
                            *matching_group = m;
                        }
                        break;
                    } else {
                        m = *(*((*m).entries.tqe_prev as *mut member_list)).tqh_last;
                    }
                }
            }
            if group_matched == UNSPEC {
                let mut runas_groups: *mut gid_list = std::ptr::null_mut::<gid_list>();
                /*
                 * The runas group was not explicitly allowed by sudoers.
                 * Check whether it is one of the target user's groups.
                 */
                if (*sudo_user._runas_pw).pw_gid == (*sudo_user._runas_gr).gr_gid {
                    group_matched = ALLOW; /* runas group matches passwd db */
                } else {
                    runas_groups = runas_getgroups();
                    if !runas_groups.is_null() {
                        let mut i: libc::c_int = 0;

                        while i < (*runas_groups).ngids {
                            if *((*runas_groups).gids).offset(i as isize)
                                == (*sudo_user._runas_gr).gr_gid
                            {
                                group_matched = ALLOW; /* matched aux group vector */
                                break;
                            } else {
                                i += 1;
                            }
                        }
                        sudo_gidlist_delref(runas_groups);
                    }
                }
            }
        }

        if user_matched == DENY || group_matched == DENY {
            debug_return_int!(DENY);
        }
        if user_matched == group_matched || (sudo_user._runas_gr).is_null() {
            debug_return_int!(user_matched);
        }
        debug_return_int!(UNSPEC);
    } //unsafe
}

/*
 * Check for lhost and shost in a list of members.
 * Returns ALLOW, DENY or UNSPEC.
 */
fn hostlist_matches_int(
    mut parse_tree: *mut sudoers_parse_tree,
    mut pw: *const passwd,
    mut lhost: *const libc::c_char,
    mut shost: *const libc::c_char,
    mut list: *const member_list,
) -> libc::c_int {
    let mut m: *mut member = std::ptr::null_mut::<member>();
    let mut matched: libc::c_int = UNSPEC;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        m = *(*((*list).tqh_last as *mut member_list)).tqh_last;
        while !m.is_null() {
            matched = host_matches(parse_tree, pw, lhost, shost, m);
            if matched != UNSPEC {
                break;
            }
            m = *(*((*m).entries.tqe_prev as *mut member_list)).tqh_last;
        }

        debug_return_int!(matched);
    } //unsafe
}

/*
 * Check for user_runhost and user_srunhost in a list of members.
 * Returns ALLOW, DENY or UNSPEC.
 */
#[no_mangle]
pub fn hostlist_matches(
    mut parse_tree: *mut sudoers_parse_tree,
    mut pw: *const passwd,
    mut list: *const member_list,
) -> libc::c_int {
    unsafe {
        let mut lhost: *const libc::c_char = if !((*parse_tree).lhost).is_null() {
            (*parse_tree).lhost
        } else {
            sudo_user.runhost as *const libc::c_char
        };
        let mut shost: *const libc::c_char = if !((*parse_tree).shost).is_null() {
            (*parse_tree).shost
        } else {
            sudo_user.srunhost as *const libc::c_char
        };

        hostlist_matches_int(parse_tree, pw, lhost, shost, list)
    } //unsafe
}

/*
 * Check whether host or shost matches member.
 * Returns ALLOW, DENY or UNSPEC.
 */
#[no_mangle]
pub fn host_matches(
    mut parse_tree: *mut sudoers_parse_tree,
    mut pw: *const passwd,
    mut lhost: *const libc::c_char,
    mut shost: *const libc::c_char,
    mut m: *const member,
) -> libc::c_int {
    let mut a: *mut alias = std::ptr::null_mut::<alias>();
    let mut matched: libc::c_int = UNSPEC;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        loop {
            match (*m).type0 as libc::c_int {
                ALL => {
                    matched = ((*m).negated == 0) as libc::c_int;
                }
                NETGROUP => {
                    if netgr_matches(
                        (*m).name,
                        lhost,
                        shost,
                        if def_netgroup_tuple!() != 0 {
                            (*pw).pw_name
                        } else {
                            std::ptr::null_mut::<libc::c_char>()
                        },
                    ) {
                        matched = ((*m).negated == 0) as libc::c_int;
                    }
                }
                NTWKADDR => {
                    if addr_matches((*m).name) {
                        matched = ((*m).negated == 0) as libc::c_int;
                    }
                }
                ALIAS | WORD => {
                    if (*m).type0 as libc::c_int == ALIAS {
                        a = alias_get(parse_tree, (*m).name, HOSTALIAS);
                        if !a.is_null() {
                            /* XXX */
                            let mut rc: libc::c_int =
                                hostlist_matches_int(parse_tree, pw, lhost, shost, &(*a).members);
                            if rc != UNSPEC {
                                matched = if (*m).negated as libc::c_int != 0 {
                                    (rc == 0) as libc::c_int
                                } else {
                                    rc
                                };
                            }
                            alias_put(a);
                            break;
                        }
                    }
                    if hostname_matches(shost, lhost, (*m).name) {
                        matched = ((*m).negated == 0) as libc::c_int;
                    }
                }
                _ => {}
            }
            break;
        }

        debug_return_int!(matched);
    } //unsafe
}

/*
 * Check for cmnd and args in a list of members.
 * Returns ALLOW, DENY or UNSPEC.
 */
#[no_mangle]
pub fn cmndlist_matches(
    mut parse_tree: *mut sudoers_parse_tree,
    mut list: *const member_list,
) -> libc::c_int {
    let mut m: *mut member = std::ptr::null_mut::<member>();
    let mut matched: libc::c_int = UNSPEC;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        m = *(*((*list).tqh_last as *mut member_list)).tqh_last;
        while !m.is_null() {
            matched = cmnd_matches(parse_tree, m);
            if matched != UNSPEC {
                break;
            }
            m = *(*((*m).entries.tqe_prev as *mut member_list)).tqh_last
        }
        debug_return_int!(matched);
    } //unsafe
}

/*
 * Check cmnd and args.
 * Returns ALLOW, DENY or UNSPEC.
 */
#[no_mangle]
pub fn cmnd_matches(mut parse_tree: *mut sudoers_parse_tree, mut m: *const member) -> libc::c_int {
    let mut a: *mut alias = std::ptr::null_mut::<alias>();
    let mut c: *mut sudo_command = std::ptr::null_mut::<sudo_command>();
    let mut rc: libc::c_int = 0;
    let mut matched: libc::c_int = UNSPEC;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        match (*m).type0 as libc::c_int {
            ALL => {
                matched = ((*m).negated == 0) as libc::c_int;
            }
            ALIAS => {
                a = alias_get(parse_tree, (*m).name, CMNDALIAS);
                if !a.is_null() {
                    rc = cmndlist_matches(parse_tree, &(*a).members);
                    if rc != UNSPEC {
                        matched = if (*m).negated as libc::c_int != 0 {
                            (rc == 0) as libc::c_int
                        } else {
                            rc
                        };
                    }
                    alias_put(a);
                }
            }
            COMMAND => {
                c = (*m).name as *mut sudo_command;
                if command_matches((*c).cmnd, (*c).args, (*c).digest) {
                    matched = ((*m).negated == 0) as libc::c_int;
                }
            }
            _ => {}
        }
        debug_return_int!(matched);
    } //unsafe
}

/*
 * Returns true if the hostname matches the pattern, else false
 */
#[no_mangle]
pub fn hostname_matches(
    mut shost: *const libc::c_char,
    mut lhost: *const libc::c_char,
    mut pattern: *const libc::c_char,
) -> bool {
    let mut host: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut rc: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        host = if !(strchr(pattern, '.' as i32)).is_null() {
            lhost
        } else {
            shost
        };
        if !(strpbrk(pattern, b"\\?*[]\0" as *const u8 as *const libc::c_char)).is_null() {
            rc = fnmatch(pattern, host, FNM_CASEFOLD) == 0;
        } else {
            rc = strcasecmp(host, pattern) == 0;
        }
        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"host %s matches sudoers pattern %s: %s\0" as *const u8 as *const libc::c_char,
            host,
            pattern,
            if rc as libc::c_int != 0 {
                b"true\0" as *const u8 as *const libc::c_char
            } else {
                b"false\0" as *const u8 as *const libc::c_char
            }
        );

        debug_return_bool!(rc);
    } //unsafe
}

/*
 * Returns true if the user/uid from sudoers matches the specified user/uid,
 * else returns false.
 */
#[no_mangle]
pub fn userpw_matches(
    mut sudoers_user: *const libc::c_char,
    mut user: *const libc::c_char,
    mut pw: *const passwd,
) -> bool {
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut uid: uid_t = 0;
    let mut rc: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        'done: loop {
            if !pw.is_null() && *sudoers_user as libc::c_int == '#' as i32 {
                uid = sudo_strtoid_v2(sudoers_user.offset(1_isize), &mut errstr);
                if errstr.is_null() && uid == (*pw).pw_uid {
                    rc = true;
                    break 'done;
                }
            }
            if def_case_insensitive_user!() != 0 {
                rc = strcasecmp(sudoers_user, user) == 0;
            } else {
                rc = strcmp(sudoers_user, user) == 0;
            }
            break;
        }

        //done:
        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"user %s matches sudoers user %s: %s\0" as *const u8 as *const libc::c_char,
            user,
            sudoers_user,
            if rc as libc::c_int != 0 {
                b"true\0" as *const u8 as *const libc::c_char
            } else {
                b"false\0" as *const u8 as *const libc::c_char
            }
        );
        debug_return_bool!(rc);
    } //unsafe
}

/*
 * Returns true if the group/gid from sudoers matches the specified group/gid,
 * else returns false.
 */
#[no_mangle]
pub fn group_matches(mut sudoers_group: *const libc::c_char, mut gr: *const group) -> bool {
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut gid: gid_t = 0;
    let mut rc: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        'done: loop {
            if *sudoers_group as libc::c_int == '#' as i32 {
                gid = sudo_strtoid_v2(sudoers_group.offset(1_isize), &mut errstr);
                if errstr.is_null() && gid == (*gr).gr_gid {
                    rc = true;
                    break 'done;
                }
            }
            if def_case_insensitive_group!() != 0 {
                rc = strcasecmp(sudoers_group, (*gr).gr_name) == 0;
            } else {
                rc = strcmp(sudoers_group, (*gr).gr_name) == 0;
            }
            break;
        }

        //done:
        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"group %s matches sudoers group %s: %s\0" as *const u8 as *const libc::c_char,
            (*gr).gr_name,
            sudoers_group,
            if rc as libc::c_int != 0 {
                b"true\0" as *const u8 as *const libc::c_char
            } else {
                b"false\0" as *const u8 as *const libc::c_char
            }
        );
        debug_return_bool!(rc);
    } //unsafe
}

/*
 * Returns true if the given user belongs to the named group,
 * else returns false.
 */
#[no_mangle]
pub fn usergr_matches(
    mut group: *const libc::c_char,
    mut user: *const libc::c_char,
    mut pw: *const passwd,
) -> bool {
    let mut matched: bool = false;
    let mut pw0: *mut passwd = std::ptr::null_mut::<passwd>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        'done: loop {
            /* Make sure we have a valid usergroup, sudo style */
            let fresh0 = group;
            group = group.offset(1);
            if *fresh0 as libc::c_int != '%' as i32 {
                sudo_debug_printf!(
                    SUDO_DEBUG_DIAG,
                    b"user group %s has no leading '%%'\0" as *const u8 as *const libc::c_char,
                    group
                );
                break 'done;
            }

            /* Query group plugin for %:name groups. */
            if *group as libc::c_int == ':' as i32 && !(def_group_plugin!()).is_null() {
                if group_plugin_query(user, group.offset(1_isize), pw) == true as libc::c_int {
                    matched = true;
                }
                break 'done;
            }

            /* Look up user's primary gid in the passwd file. */
            if pw.is_null() {
                pw0 = sudo_getpwnam(user);
                if pw0.is_null() {
                    sudo_debug_printf!(
                        SUDO_DEBUG_DIAG,
                        b"unable to find %s in passwd db\0" as *const u8 as *const libc::c_char,
                        user
                    );
                    break 'done;
                }
                pw = pw0;
            }

            if user_in_group(pw, group) {
                matched = true;
                break 'done;
            }

            /* Query the group plugin for Unix groups too? */
            /*if !(def_group_plugin!()).is_null() && def_always_query_group_plugin!() != 0 {
                if group_plugin_query(user, group, pw) == true as libc::c_int {
                    matched = true;
                    break 'done;
                }
            }*/
            if !(def_group_plugin!()).is_null()
                && def_always_query_group_plugin!() != 0
                && group_plugin_query(user, group, pw) == true as libc::c_int
            {
                matched = true;
                break 'done;
            }

            break;
        }

        //done:
        if !pw0.is_null() {
            sudo_pw_delref(pw0);
        }

        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"user %s matches group %s: %s\0" as *const u8 as *const libc::c_char,
            user,
            group,
            if matched as libc::c_int != 0 {
                b"true\0" as *const u8 as *const libc::c_char
            } else {
                b"false\0" as *const u8 as *const libc::c_char
            }
        );

        debug_return_bool!(matched);
    } //unsafe
}

/*
 * Check the domain for invalid characters.
 * Linux getdomainname(2) returns (none) if no domain is set.
 */
fn valid_domain(mut domain: *const libc::c_char) -> bool {
    let mut cp: *const libc::c_char = std::ptr::null::<libc::c_char>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        cp = domain;
        while *cp as libc::c_int != '\0' as i32 {
            /* Check for illegal characters, Linux may use "(none)". */
            if *cp as libc::c_int == '(' as i32
                || *cp as libc::c_int == ')' as i32
                || *cp as libc::c_int == ',' as i32
                || *cp as libc::c_int == ' ' as i32
            {
                break;
            }
            cp = cp.offset(1);
        }
        if cp == domain || *cp as libc::c_int != '\0' as i32 {
            debug_return_bool!(false);
        }
        debug_return_bool!(true);
    } //unsafe
}

/*
 * Get NIS-style domain name and copy from static storage or NULL if none.
 */
#[no_mangle]
pub fn sudo_getdomainname() -> *const libc::c_char {
    static mut domain: *mut libc::c_char = 0 as *const libc::c_char as *mut libc::c_char;
    static mut initialized: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        if !initialized {
            let mut host_name_max: size_t = 0;
            let mut rc: libc::c_int = 0;

            host_name_max = sysconf(_SC_HOST_NAME_MAX as libc::c_int) as size_t;
            if host_name_max == -(1 as libc::c_int) as size_t {
                host_name_max = 255 as size_t; /* POSIX and historic BSD */
            }

            domain = malloc(host_name_max.wrapping_add(1 as libc::c_ulong)) as *mut libc::c_char;
            if !domain.is_null() {
                *domain.offset(0_isize) = '\0' as i32 as libc::c_char;
                rc = getdomainname(domain, host_name_max.wrapping_add(1 as libc::c_ulong));
                if rc == -(1 as libc::c_int) || !valid_domain(domain) {
                    /* Error or invalid domain name. */
                    free(domain as *mut libc::c_void);
                    domain = std::ptr::null_mut::<libc::c_char>();
                }
            } else {
                /* XXX - want to pass error back to caller */
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR | SUDO_DEBUG_LINENO,
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
            }
            initialized = true;
        }

        debug_return_str!(domain);
    } //unsafe
}

/*
 * Returns true if "host" and "user" belong to the netgroup "netgr",
 * else return false.  Either of "lhost", "shost" or "user" may be NULL
 * in which case that argument is not checked...
 */
#[no_mangle]
pub fn netgr_matches(
    mut netgr: *const libc::c_char,
    mut lhost: *const libc::c_char,
    mut shost: *const libc::c_char,
    mut user: *const libc::c_char,
) -> bool {
    let mut domain: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut rc: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());

        if def_use_netgroups!() == 0 {
            sudo_debug_printf!(
                SUDO_DEBUG_INFO,
                b"netgroups are disabled\0" as *const u8 as *const libc::c_char
            );
            debug_return_bool!(false);
        }

        /* make sure we have a valid netgroup, sudo style */
        netgr = netgr.offset(1);
        if *netgr as libc::c_int != '+' as i32 {
            sudo_debug_printf!(
                SUDO_DEBUG_DIAG,
                b"netgroup %s has no leading '+'\0" as *const u8 as *const libc::c_char,
                netgr
            );
            debug_return_bool!(false);
        }

        /* get the domain name (if any) */
        domain = sudo_getdomainname();

        if innetgr(netgr, lhost, user, domain) != 0 {
            rc = true;
        } else if lhost != shost && innetgr(netgr, shost, user, domain) != 0 {
            rc = true;
        }

        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"netgroup %s matches (%s|%s, %s, %s): %s\0" as *const u8 as *const libc::c_char,
            netgr,
            if !lhost.is_null() {
                lhost
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            if !shost.is_null() {
                shost
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            if !user.is_null() {
                user
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            if !domain.is_null() {
                domain
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            if rc as libc::c_int != 0 {
                b"true\0" as *const u8 as *const libc::c_char
            } else {
                b"false\0" as *const u8 as *const libc::c_char
            }
        );

        debug_return_bool!(rc);
    } //unsafe
}
